﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
using SimplePlanner.Entity;

namespace SimplePlanner.Logic
{
    public class FetchingService
    {

        private static string USER_AGENT = Properties.Resources.USER_AGENT;
        private static string TEXT_HTML_UTF8 = Properties.Resources.TEXT_HTML_UTF8;
        private static string URL_ENCODED = Properties.Resources.URL_ENCODED;

        private static string scheduleSelectUriString = Properties.Resources.scheduleSelectUriString;
        private static string scheduleUriString = Properties.Resources.scheduleUriString;

        private static Uri scheduleSelectUri = new Uri(scheduleSelectUriString);
        private static Uri scheduleUri = new Uri(scheduleUriString);
        private static RegexOptions regexOptions = RegexOptions.Singleline | RegexOptions.IgnoreCase;

        public static void FetchNTUData()
        {
            //GetPlannerWebpages();
            ProcessScheduleWebpages();            
        }

        public static void ProcessScheduleWebpages(string path = "")
        {
            // Get Files 
            if (path.Equals(""))
                path = Directory.GetCurrentDirectory() + "\\Webpages";

            Directory.CreateDirectory(path);
            path += "\\";
            string[] files = Directory.GetFiles(path);
            string savepath = path + "db\\";

            foreach (var filepath in files)
            {
                ProcessSchduleWebpageFile(filepath, savepath);

            }
        }

        private static void ProcessSchduleWebpageFile(string filepath, string savepath)
        {
            string majorName = getFileName(filepath);
            
            string html = File.ReadAllText(filepath);
            // Get a Subject Table
            string[] subjectsHtml = getSubjectsHtmlFromHtml(html);

            foreach (var subjectHtml in subjectsHtml)
            {
                Subject subject = createSubjectFromHtml(subjectHtml);
                outputSubject(subject, savepath + subject.name + ".subject");
            }

        }

        
        private static Subject createSubjectFromHtml(string subjectHtml)
        {
            Subject subject = new Subject();
            subject.name = getSubjectNameFromHtml(subjectHtml);
            string[] indexesHtml = GetIndexesHtmlFromSubjectHtml(subjectHtml);
            foreach (var indexHtml in indexesHtml)
            {
                Index index = createIndexFromHtml(indexHtml);
                subject.addIndex(index);
            }
            return subject;
        }

        private static Index createIndexFromHtml(string indexHtml)
        {
            Index index = new Index();
            index.code = getIndexCodeFromHtml(indexHtml);
            string[] periodsHtml = GetPeriodsHtmlFromIndexHtml(indexHtml);
            foreach (var periodHtml in periodsHtml)
            {
                Period period = createPeriodFromHtml(periodHtml);
                index.addPeriod(period);
            }
            return index;
        }

        private static Period createPeriodFromHtml(string periodHtml)
        {
            Regex regex = new Regex(Properties.Resources.RegexGetPeriodFromHtml, regexOptions);
            Match match = regex.Match(periodHtml);
            Period period = new Period
            {
                day = day2int(match.Groups["day"].Value),
                startTime = str2time(match.Groups["start"].Value),
                endTime = str2time(match.Groups["end"].Value),
                week = str2week(match.Groups["week"].Value)
            };
            return period;    
            
        }

        private static DayTime str2time(string str)
        {
            int h, m;
            h = Convert.ToInt32(str.Substring(0, 2));
            m = Convert.ToInt32(str.Substring(2));
         
            return (new DayTime(h, m));
        }

        private static int day2int(string str)
        {
            if (str[0] == 'M')
                return 1;
            else
                if (str[0] == 'T')
                {
                    if (str[1] == 'U')
                        return 2;
                    else
                        return 4;
                }
                else
                    if (str[0] == 'W')
                        return 3;
                    else
                        if (str[0] == 'F')
                            return 5;
                        else
                            if (str[0] == 'S')
                            {
                                if (str[1] == 'A')
                                    return 6;
                            }
            return 0;
        }

        private static int str2week(string str)
        {
            int wk;
            if (str.StartsWith("Wk1,"))
                wk = 1;
            else
                if (str.StartsWith("Wk2,"))
                    wk = 2;
                else
                    wk = 0;
            return wk;
        }

        private static string getIndexCodeFromHtml(string indexHtml)
        {
            Regex regex = new Regex(Properties.Resources.RegexGetIndexCodeFromHtml, regexOptions);
            return regex.Match(indexHtml).Groups["index"].Value;
        }

        private static string getSubjectNameFromHtml(string subjectHtml)
        {
            Regex regex = new Regex(Properties.Resources.RegexGetSubjectNameFromHtml, regexOptions);
            Match match = regex.Match(subjectHtml);
            return match.Groups["name"].Value;
        }

        // TODO
        private static string[] GetPeriodsHtmlFromIndexHtml(string indexHtml)
        {
            List<string> ret = new List<string>();
            Regex regex = new Regex(Properties.Resources.RegexGetPeriodsHtmlFromIndexHtml, regexOptions);
            MatchCollection matches = regex.Matches(indexHtml);

            foreach (Match match in matches)
            {
                ret.Add(match.Value);
            }

            return ret.ToArray();
        }

        private static string[] GetIndexesHtmlFromSubjectHtml(string subjectHtml)
        {
            List<string> ret = new List<string>();
            Regex regex = new Regex(Properties.Resources.RegexGetIndexesHtmlFromSubjectHtml, RegexOptions.IgnoreCase | RegexOptions.Multiline);
            MatchCollection matches = regex.Matches(subjectHtml);

            foreach (Match match in matches)
            {
                ret.Add(match.Value);
            }
            
            return ret.ToArray();
        }

        private static string[] getSubjectsHtmlFromHtml(string html)
        {
            List<string> ret = new List<string>();
            Regex regex = new Regex(Properties.Resources.RegexGetSubjectsHtmlFromHtml, regexOptions);
            MatchCollection matches = regex.Matches(html);

            foreach(Match match in matches)
            {
                ret.Add(match.Value);
            }
               
            return ret.ToArray();
        }

        private static string getFileName(string path)
        {
            FileInfo fi = new FileInfo(path);
            
            return fi.Name.Substring(0, fi.Name.LastIndexOf('.') );
        }

        private static void outputSubject(Subject sub, string outputPath)
        {
            /*Original
            string path = outputPath;
            StreamReader sr;
            string text = "";
            if (File.Exists(path))
            {
                sr = new StreamReader(path);
                text = sr.ReadToEnd();
                sr.Close();
            }
            StreamWriter sw = new StreamWriter(path, true);

            // index format: index_number periods
            //               wk start end
            foreach (Index i in sub.indexes)
            {
                if (text.IndexOf(i.code) != -1)
                    continue;
                sw.WriteLine(i.code + " " + i.periods.Count);
                foreach (Period p in i.periods)
                {
                    sw.WriteLine(p.week + " " + p.day + " " +
                        p.startTime.hour + " " + p.startTime.minute + " "
                        + p.endTime.hour + " " + p.endTime.minute);
                }
            }
            sw.Close();
             * */
            StringBuilder sb = new StringBuilder();

            foreach (Index i in sub.indexes)
            {
                sb.AppendLine(i.code + " " + i.periods.Count);
                foreach (Period p in i.periods)
                {
                    sb.AppendLine(p.week + " " + p.day + " " +
                        p.startTime.hour + " " + p.startTime.minute + " "
                        + p.endTime.hour + " " + p.endTime.minute);
                }
            }
            FileService.OutputStringToFile(sb.ToString(), outputPath);
        }


        public static void GetPlannerWebpages(string savepath = "")
        {
            if (savepath.Equals(""))
                savepath = Directory.GetCurrentDirectory() + "\\Webpages";

            Directory.CreateDirectory(savepath);
            savepath += "\\";
            // direct to Load Schedule webpage
            //Uri scheduleUri = null;
            string scheduleSelectPageHtml = getHtmlFromUri(scheduleSelectUri);

            // get all the options
            // KeyValuePair Format: MajorName, 
            List<KeyValuePair<string, string>> majors = getMajorsFromWebpage(scheduleSelectPageHtml);

            // loop every option
            foreach (KeyValuePair<string, string> majorKV in majors)
            {
                // generate http post 
                string scheduleHtml = gethtmlFromSchedule(majorKV);

                // TODO: Process Webpage content

                // save data
                string filepath = savepath + majorKV.Key + ".html";
                FileService.OutputStringToFile(scheduleHtml, filepath);
            }

        }

        private static string getPostHtmlFromUri(string uri, List<KeyValuePair<string, string>> postData)
        {
            //Uri scheduleUri = new Uri(scheduleUriString);

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(scheduleUri);
            request.Method = "POST";
            // request.ContentType = URL_ENCODED;
            // request.UserAgent = USER_AGENT;
            AttachPostData(request, postData);

            // save data
            string webpageContent = getHtmlFromHttpWebRequest(request);
            return webpageContent;

        }

        private static string gethtmlFromSchedule(KeyValuePair<string, string> majorKV)
        {
            //Uri scheduleUri = new Uri(scheduleUriString);

            List<KeyValuePair<string, string>> postData = new List<KeyValuePair<string, string>>();
            postData.Add(new KeyValuePair<string, string>("acadsem", Properties.Resources.acadsem));
            postData.Add(new KeyValuePair<string, string>("boption", "CLoad"));
            postData.Add(new KeyValuePair<string, string>("r_course_yr", majorKV.Value));
            postData.Add(new KeyValuePair<string, string>("r_search_type", "F"));
            postData.Add(new KeyValuePair<string, string>("r_subj_code", "Enter Keywords or Course Code"));
            postData.Add(new KeyValuePair<string, string>("staff_access", "false"));
            // save data

            string webpageContent = getPostHtmlFromUri(scheduleUriString, postData);

            return webpageContent;
        }

        private static void AttachPostData(HttpWebRequest request, List<KeyValuePair<string, string>> fields)
        {
            StringBuilder data = new StringBuilder();
            bool prepend = false;
            foreach (KeyValuePair<string, string> field in fields)
            {
                if (prepend)
                    data.Append("&");
                else
                    prepend = true;

                data.Append(UrlEncode(field.Key))
                    .Append("=")
                    .Append(UrlEncode(field.Value));

            }

            byte[] encodedRequest = Encoding.UTF8.GetBytes(data.ToString());
            request.ContentLength = encodedRequest.Length;
            using (Stream requestStream = request.GetRequestStream())
                requestStream.Write(encodedRequest, 0, encodedRequest.Length);
        }


        private static string UrlEncode(string p)
        {
            return HttpUtility.UrlEncode(p);
        }

        static string getHtmlFromHttpWebRequest(HttpWebRequest request)
        {
            using (WebResponse response = request.GetResponse())
            using (Stream responseStream = response.GetResponseStream())
            using (StreamReader streamReader = new StreamReader(responseStream, true))
                return streamReader.ReadToEnd();
        }

        private static void saveStringToFile(string webpageContent, string filepath)
        {
            StreamWriter sw = new StreamWriter(filepath);
            sw.Write(webpageContent);
            sw.Close();
        }

        private static string getHtmlFromUri(Uri uri)
        {
            string ret;
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
            ret = getHtmlFromHttpWebRequest(request);
            return ret;
        }

        private static List<KeyValuePair<string, string>> getMajorsFromWebpage(string loadSchedulePage)
        {
            List<KeyValuePair<string, string>> ret = new List<KeyValuePair<string, string>>();
            Regex regex = null;

            // get category Html
            regex = new Regex(Properties.Resources.RegexGetCategoryHtml,
                RegexOptions.Singleline | RegexOptions.IgnoreCase);
            string majorsHtml = singleRegexMatch(loadSchedulePage, regex);

            // get options from major html
            regex = new Regex(@"<OPTION\b.*?\bvalue=(?<code>.*?)>(?<name>.*?)\n",
                RegexOptions.Singleline | RegexOptions.IgnoreCase);
            string key = "name", value = "code";
            List<KeyValuePair<string, string>> majors = GetKVPairsFromHtmlUsingRegex(majorsHtml, regex, key, value);

            return majors;

        }

        private static string getMajorClassInfoFromWebpageUsingKVP(KeyValuePair<string, string> major)
        {


            throw new NotImplementedException();
        }

        private static List<KeyValuePair<string, string>> GetKVPairsFromHtmlUsingRegex(string majorHtml, Regex regex, string key, string value)
        {
            List<KeyValuePair<string, string>> ret = new List<KeyValuePair<string, string>>();

            MatchCollection matches = regex.Matches(majorHtml);

            foreach (Match match in matches)
            {
                string name = match.Groups["name"].Value;
                string code = match.Groups["code"].Value;
                if (name.Trim() != "" && code.Trim() != "" && !name.StartsWith("---"))
                    ret.Add(new KeyValuePair<string, string>(name, code));

            }
            return ret;
        }


        private static string singleRegexMatch(string webpageContent, Regex regex)
        {
            Match match = regex.Match(webpageContent);
            if (match.Groups.Count == 1)
                return match.Groups[0].Value.ToString();
            else
                return String.Empty;
        }

        private static string getLoadSchedulePage()
        {
            throw new NotImplementedException();
        }


    }
}
